package com.ngc.mall.service.impl;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ngc.mall.bo.SignIntegralConfigurationBO;
import com.ngc.mall.common.configure.exception.BusinessException;
import com.ngc.mall.common.configure.mybatisPlus.BaseServiceImpl;
import com.ngc.mall.common.configure.response.ResponseCode;
import com.ngc.mall.core.enumeration.IntegralTaskType;
import com.ngc.mall.core.enumeration.IntegralType;
import com.ngc.mall.domain.IntegralSpuDomain;
import com.ngc.mall.domain.UserIntegralDomain;
import com.ngc.mall.domain.UserIntegralLogDomain;
import com.ngc.mall.domain.UserIntegralTaskDomain;
import com.ngc.mall.manager.redis.RedisKeyConstant;
import com.ngc.mall.mapper.UserIntegralLogMapper;
import com.ngc.mall.service.*;
import com.ngc.mall.vo.UserIntegralVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 *
 * 用户-积分流水明细实现类
 * @author: chenxiaolin
 * @date 2023/9/13 16:17
**/
@Service
public class UserIntegralLogServiceImpl extends BaseServiceImpl<UserIntegralLogMapper, UserIntegralLogDomain> implements IUserIntegralLogService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private IUserIntegralService iUserIntegralService;

    @Autowired
    private IUserIntegralTaskService iUserIntegralTaskService;

    @Autowired
    private IIntegralSpuService iIntegralSpuService;

    @Autowired
    private ICouponService iCouponService;

    @Transactional
    public Boolean sign(Long uid, LocalDateTime now) {
        long key=Long.parseLong(now.format(DateTimeFormatter.ofPattern("yyyyMM")));
        String signKey = String.format(RedisKeyConstant.USER_SIGN_IN, key, uid);
        int dayOfMonth = now.getDayOfMonth();
        if (stringRedisTemplate.opsForValue().getBit(signKey, dayOfMonth-1)) {
            throw new BusinessException(ResponseCode.USER_SIGNED);
        }
        stringRedisTemplate.opsForValue().setBit(signKey, dayOfMonth-1, true);
        int continuousSignCount = this.getContinuousSignCount(uid,now);
        return this.doSaveUserIntegral(uid, continuousSignCount,now);

    }

    /**
     * 获取连续签到天数
     *
     * @return
     */
    public Integer getContinuousSignCount(Long uid,LocalDateTime now) {
        long key=Long.parseLong(now.format(DateTimeFormatter.ofPattern("yyyyMM")));
        String signKey = String.format(RedisKeyConstant.USER_SIGN_IN, key, uid);
        //4. 获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth();
        //5. 获取本月截至今天为止的所有的签到记录，返回的是一个十进制的数字 BITFIELD sign:5:202301 GET u3 0
        List<Long> result = stringRedisTemplate.opsForValue().bitField(
                signKey,
                BitFieldSubCommands.create()
                        .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        //没有任务签到结果
        if (result == null || result.isEmpty()) {
            return 0;
        }
        Long num = result.get(0);
        if (num == null || num == 0) {
            return 0;
        }
        //6. 循环遍历
        int count = 0;
        while (true) {
            //6.1 让这个数字与1 做与运算，得到数字的最后一个bit位 判断这个数字是否为0
            if ((num & 1) == 0) {
                //如果为0，签到结束
                break;
            } else {
                count++;
            }
            num >>>= 1;
        }
        return count;
    }

    /**
     * 新增积分
     *
     * @return
     */
    public Boolean doSaveUserIntegral(Long uid, int continuousSignCount,LocalDateTime now) {
        ///累加签到次数，
        int integral = 0;
        UserIntegralLogDomain userIntegralLog = null;
        ///判断累加后的积分是否满足设置的连续签到积分条件,否则+1（普通签到积分数），是则+10(5天连续签到赠送的积分数）
        if (stringRedisTemplate.opsForHash().get("SIGN_CONFIGURATION", String.valueOf(continuousSignCount)) == null) {
            integral = 1;
            userIntegralLog = UserIntegralLogDomain.builder().integral(integral).userId(uid).integralType(IntegralType.SIGN.getValue()).build();
            ///更新用户-总积分表
            iUserIntegralService.updateUserIntegral(uid, integral);
            ///创建用户-积分明细表
            Long id = super.create(userIntegralLog);
            if (id == null) {
                System.out.println("插入明细失败");
                return false;
            }
            return true;
        }
        Object object =stringRedisTemplate.opsForHash().get("SIGN_CONFIGURATION",String.valueOf(continuousSignCount));



        if(object!=null){
            SignIntegralConfigurationBO bo= JSONUtil.toBean((String)object,SignIntegralConfigurationBO.class);
            integral=bo.getIntegral();
            userIntegralLog = UserIntegralLogDomain.builder().integral(integral).userId(uid).integralType(IntegralType.CONTINUOUS_SIGN.getValue()).build();
            ///更新用户-总积分表
            iUserIntegralService.updateUserIntegral(uid, integral);
            ///创建用户-积分明细表
            Long id = super.create(userIntegralLog);
            if (id == null) {
                System.out.println("插入明细失败");
                return false;
            }
            return true;
        }
        return false;
    }

    /**
     * 减少积分
     *
     * @return
     */
    public void doRemoveUserIntegral(Long uid, Integer integral) {
        ///更新用户-总积分表

        ///创建用户-积分明细表
    }

    /**
     * 签到-积分配置
     *
     * @return
     */
    public void saveSignIntegralConfiguration() {
//        HashMap<Integer, SignIntegralConfigurationBO> map = new HashMap<>();
//        map.put(5, SignIntegralConfigurationBO.builder().days(5).integral(10).title("赠送10个积分").build());
//        map.put(7, SignIntegralConfigurationBO.builder().days(7).integral(30).title("赠送30个积分").build());
//        map.put(15, SignIntegralConfigurationBO.builder().days(15).integral(50).title("赠送50个积分").build());
//        map.put(30, SignIntegralConfigurationBO.builder().days(30).integral(100).title("赠送100个积分").build());
//        stringRedisTemplate.opsForHash().putAll("SIGN_CONFIGURATION", map);
    }

    /**
     * 获取个人积分详情
     * @return
     */
    public UserIntegralVO getUserIntegralDetail(Long uid,LocalDateTime now){
        UserIntegralDomain userIntegralDomain=iUserIntegralService.getByUid(uid);
        int integralTotal;
        integralTotal=userIntegralDomain.getIntegralTotal();
        int continuousSignCount=this.getContinuousSignCount(uid,now);
        this.monthSigned(now,uid);
        Map<String,Object> monthSignedResult=this.monthSigned(now,uid);
        System.out.println(monthSignedResult);
        TreeMap<Integer,Integer> monthSigned= (TreeMap<Integer, Integer>) monthSignedResult.get("signCalendar");
        Integer todaySignFlag= (Integer) monthSignedResult.get("todaySignFlag");
        UserIntegralVO userIntegralVO=UserIntegralVO.builder().continuousSignCount(continuousSignCount).integralTotal(integralTotal).integralRecord(monthSigned).todaySignFlag(todaySignFlag).month(now.getMonthValue()).day(now.getDayOfMonth()).build();
        System.out.println(userIntegralVO);
        return userIntegralVO;
    }

    @Override
    public List<UserIntegralLogDomain> queryAllUserIntegral() {
        return super.list();
    }

    /**
     * 获取月度签到情况
     * @param now
     * @param uid
     * @return
     */
    public Map<String, Object> monthSigned(LocalDateTime now, Long uid){
        Map<String, Object> result = new HashMap<>(2);
        long key=Long.parseLong(now.format(DateTimeFormatter.ofPattern("yyyyMM")));
        String signKey = String.format(RedisKeyConstant.USER_SIGN_IN, key, uid);
        //4. 获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth();
        //5. 获取本月截至今天为止的所有的签到记录，返回的是一个十进制的数字 BITFIELD sign:5:202301 GET u3 0
        LocalDate date = LocalDate.of(now.getYear(),now.getMonthValue(), 1);
        List<Long> list = stringRedisTemplate.opsForValue().bitField(
                signKey,
                BitFieldSubCommands.create()
                        .get(BitFieldSubCommands.BitFieldType.unsigned(date.lengthOfMonth())).valueAt(0));
        boolean signFlag = Boolean.FALSE;
        TreeMap<Integer, Integer> signMap = new TreeMap<>();
        if (list != null && list.size() > 0) {
            // 由低位到高位，为0表示未签，为1表示已签
            long v = list.get(0) == null ? 0 : list.get(0);
            //循环次数为当月的天数
            for (int i = date.lengthOfMonth(); i > 0; i--) {
                LocalDate d = date.withDayOfMonth(i);
                int type = 0;
                if (v >> 1 << 1 != v) {
                    //状态为正常签到
                    type = 1;
                    //这里和当前日期对比,方便前端特殊标记今天是否签到
                    if (d.compareTo(LocalDate.now()) == 0) {
                        signFlag = Boolean.TRUE;
                    }
                }
//                if (keys.contains(d.getDayOfMonth() + "")) {
//                    //状态为补签
//                    type = 2;
//                }
                //返回给前端当月的所有日期,以及签,补签或者未签的状态
                signMap.put(Integer.parseInt(d.format(DateTimeFormatter.ofPattern("dd"))), type);
                v >>= 1;
            }
        }
        System.out.println(signMap);
        result.put("signCalendar", signMap);
        result.put("todaySignFlag",signFlag?1:0);
        return result;

    }

    /**
     * 完成积分任务
     * @param uid
     * @param tag
     * @return
     */
    @Override
    @Transactional
    public Boolean saveSignInTask(Long uid,String tag,LocalDateTime now){
        int integralType = 0;
        //判断是否有这个积分任务
        UserIntegralTaskDomain taskDomain=iUserIntegralTaskService.getOne(Wrappers.<UserIntegralTaskDomain>lambdaQuery().eq(UserIntegralTaskDomain::getTaskTag,tag).eq(UserIntegralTaskDomain::getIsEnabled,true));
        if(taskDomain==null){
            throw new BusinessException(ResponseCode.INTEGRAL_TASK_NOT_EXIST);
        }
        //积分任务类型
        int taskType=taskDomain.getTaskType();

//        long today=Long.parseLong(now.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
        String today=now.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        //写入redis
        //福利任务
        if(taskType== IntegralTaskType.WELFARE.getValue()){
            String welfareKey = String.format(RedisKeyConstant.USER_SIGN_WELFARE_TASK, uid);
            if(stringRedisTemplate.opsForHash().hasKey(welfareKey,tag)){
                throw new BusinessException(ResponseCode.INTEGRAL_TASK_COMPLETED);
            }
            try{
                stringRedisTemplate.opsForHash().put(welfareKey,tag,today);
            }catch(Exception e){
                e.printStackTrace();
                throw new BusinessException(ResponseCode.FAILURE);
            }
            integralType=IntegralType.WELFARE_TASK.getValue();
        }

        //日常任务，隔天0天自动失效
        if(taskType== IntegralTaskType.DAILY.getValue()){
            Calendar curDate = Calendar.getInstance();
            String dailyKey=String.format(RedisKeyConstant.USER_SIGN_DAILY_TASK,uid);
            Calendar nextDate = new GregorianCalendar(curDate.get(Calendar.YEAR), curDate.get(Calendar.MONTH) + 1, 1, 0, 0, 0);
            int second = (int)(nextDate.getTimeInMillis() - curDate.getTimeInMillis()) / 1000;
            try {
                stringRedisTemplate.opsForHash().put(dailyKey,tag,"1");

            } catch (Exception e) {
                e.printStackTrace();
                throw new BusinessException(ResponseCode.FAILURE);
            }
            integralType=IntegralType.DAILY_TASK.getValue();
            if(!stringRedisTemplate.hasKey(dailyKey)){
                stringRedisTemplate.expire(dailyKey,60, TimeUnit.SECONDS);
            }
        }
       //增加积分，写入积分明细流水表
        super.create(UserIntegralLogDomain.builder().integralType(integralType).integral(taskDomain.getIntegral()).userId(uid).build());
        //更新个人总积分
        return iUserIntegralService.updateUserIntegral(uid,taskDomain.getIntegral());
    }

    /**
     * 查看我的今日积分任务完成情况
     */
    @Override
    public List<UserIntegralTaskDomain> querySignInTask(Long uid, LocalDateTime now){
        List<UserIntegralTaskDomain> userIntegralTaskList=iUserIntegralTaskService.list(Wrappers.<UserIntegralTaskDomain>lambdaQuery().orderByAsc(UserIntegralTaskDomain::getSort).eq(UserIntegralTaskDomain::getIsEnabled,true));
        Map<Object,Object> completeMap=new HashMap<>(userIntegralTaskList.size());
        String welfareKey=String.format(RedisKeyConstant.USER_SIGN_WELFARE_TASK,uid);
        Map<Object,Object> welfareMap=stringRedisTemplate.opsForHash().entries(welfareKey);
        System.out.println(welfareMap);
        if(welfareMap.size()>0){
            completeMap.putAll(welfareMap);
        }
        //日常任务
        String dailyKey=String.format(RedisKeyConstant.USER_SIGN_DAILY_TASK,uid);
        Map<Object,Object> dailyMap=stringRedisTemplate.opsForHash().entries(dailyKey);
        if(welfareMap.size()>0){
            completeMap.putAll(dailyMap);
        }
        userIntegralTaskList.forEach(userIntegralTaskDomain -> {
            String value= (String) completeMap.get(userIntegralTaskDomain.getTaskTag());
            if(value==null){
                userIntegralTaskDomain.setCompleteFlag(0);
            }else{
                userIntegralTaskDomain.setCompleteFlag(1);
            }
        });

        System.out.println(userIntegralTaskList);
        return userIntegralTaskList;
    }


    @Transactional
    @Override
    public Boolean exchangeIntegralSpu(Long uid,Long integralSpuId){
        //判断积分商品是否存在
        IntegralSpuDomain integralSpuDomain=iIntegralSpuService.getById(integralSpuId);
        if(integralSpuDomain==null){
            throw new BusinessException(ResponseCode.FAILURE);
        }
        int integral=integralSpuDomain.getIntegral();
        //判断该用户的总积分是否支持扣减
        UserIntegralDomain userIntegralDomain=iUserIntegralService.getByUid(uid);
        int integralTotal=userIntegralDomain.getIntegralTotal();
        if(integralTotal-integral<0){
            //总积分不足以扣减,抛出异常
            throw new BusinessException(ResponseCode.INTEGRAL_LACK);
        }
        ///扣减该用户总积分，写入UserCoupon，和积分流水没明细
        iUserIntegralService.updateUserIntegral(uid,-integral);
        super.save(UserIntegralLogDomain.builder().integral(-integral).userId(uid).integralType(IntegralType.EXCHANGE.getValue()).build());
        //目前只支持券为兑换商品（只能日期类型为天数的）
        return iCouponService.collectOneCoupon(uid,integralSpuDomain.getCouponId(),false);
    }

    @Override
    public List<UserIntegralLogDomain> findByUid(Long uid) {
        return super.list(Wrappers.<UserIntegralLogDomain>lambdaQuery().eq(UserIntegralLogDomain::getUserId,uid).orderByDesc(UserIntegralLogDomain::getCreateTime));
    }



}
